#ifndef FLOAT_VOL_GRID_H
#define FLOAT_VOL_GRID_H

#include "RawVolGrid.h"
#include "matrix.h"
#include <windows.h>
#include "OglViewInterfaces.h"

#include "polynomial.h"

class FloatVolGrid // interface
{
public:
	virtual int GetDataBounds(int) const=0;
	virtual mdsVector<2, double> GetSpaceBounds(int) const=0;
	virtual float GetValAt(int, int, int) const = 0;
	virtual float GetValAt(double, double, double) const=0;
	virtual void SetValAt(int, int, int, float) = 0;
	virtual mdsVector<3, double> EvalGradientAt(double, double, double) const=0;

	virtual void SetWorkingChannel(int nChannel)=0;

	virtual double FvgGetMin() const=0;
	virtual double FvgGetMax() const=0;

	virtual double FvgGetMean() const=0;
	virtual double FvgGetStdDev() const=0;
	virtual double FvgGetMeanSqrd() const=0;

	virtual bool GetVoxelInterpretation() const=0;

};

// typedef polynomial<4, double> SplineEditCurve;

class FloatVolGridRaw: public FloatVolGrid
{
	int m_nSize[3];
	int m_nNumChannels;
	bool m_bValid;
	bool m_bChannelFirst;
	float * m_pData;

	int m_nWorkingChannel;

	inline int GetIdx(int nX, int nY, int nZ, int nChannel) const 
	{ 
		if(m_bChannelFirst)
			return nX + m_nSize[0]*(nY + m_nSize[1]*(nZ+m_nNumChannels*nChannel)); 
		else
			return nChannel+m_nNumChannels*(nX + m_nSize[0]*(nY + m_nSize[1]*nZ)); 
	}

	double m_lfMin, m_lfMax;

	int m_nNumValidSamples;

	double m_lfMean, m_lfStdDev;
	double m_lfExpSqrd;
	mdsVector<3, double>  EvalGradientAtDataPoint(int nX, int nY, int nZ) const;

	bool m_bInterpVoxelsLiterally; // interpret voxels literally -- i.e. no xfer function

public:
	FloatVolGridRaw(void);
	~FloatVolGridRaw(void);

	void SetVoxelInterpretation(bool bInterpretVoxelsLiterally)
	{
		m_bInterpVoxelsLiterally = bInterpretVoxelsLiterally;
	}

	virtual bool GetVoxelInterpretation() const
	{
		return m_bInterpVoxelsLiterally;
	}

	inline double GetMin() const { return m_lfMin; }
	inline double GetMax() const { return m_lfMax; }

	inline double GetMean() const { return m_lfMean; }
	inline double GetStdDev() const { return m_lfStdDev; }
	inline double GetMeanSqrd() const { return m_lfExpSqrd; } // returns the mean of the squares, not the square of the mean

	virtual double FvgGetMin() const { return GetMin(); }
	virtual double FvgGetMax() const { return GetMax(); }

	virtual double FvgGetMean() const { return GetMean(); }
	virtual double FvgGetStdDev() const { return GetStdDev(); }
	virtual double FvgGetMeanSqrd() const { return GetMeanSqrd(); }

	inline int GetNumValidSamples() const { return m_nNumValidSamples; }

	bool ConvertFromRawVolGrid(const RawVolGrid & rawVolGrid, bool bIsBigEndian, bool bFlip=false);

	bool SetSize(int nX, int nY, int nZ, int nChannels);

	virtual void SetWorkingChannel(int nChannel)
	{
		m_nWorkingChannel = nChannel;
	}

	virtual int GetDataBounds(int) const;
	virtual mdsVector<2, double> GetSpaceBounds(int) const;
	virtual float GetValAt(int, int, int) const;
	virtual float GetValAt(double, double, double) const;
	virtual void SetValAt(int, int, int, float);
	virtual mdsVector<3, double> EvalGradientAt(double, double, double) const;

	bool DoProbXferFunction(FloatVolGridRaw & src, 
		SplineEditCurve redPolys[], double redKnots[], int nRedPts,
		SplineEditCurve greenPolys[], double greenKnots[], int nGreenPts,
		SplineEditCurve bluePolys[], double blueKnots[], int nBluePts, 
		SplineEditCurve alphaPolys[], double alphaKnots[], int nAlphaPts,
		double lfMinVal, double lfScale); // YUCKY messy, too many arguments
};


class FloatVolGridDownSampled: public FloatVolGrid
{
	int m_nSize[3];
	bool m_bValid;
	float * m_pData;
	inline int GetIdx(int nX, int nY, int nZ) const { return nX + m_nSize[0]*(nY + m_nSize[1]*nZ); }

	double m_lfMin, m_lfMax;

	int m_nNumValidSamples;

	int m_nWorkingChannel;

	double m_lfMean, m_lfStdDev;
	double m_lfExpSqrd;
	mdsVector<3, double>  EvalGradientAtDataPoint(int nX, int nY, int nZ) const;

public:
	FloatVolGridDownSampled(void);
	~FloatVolGridDownSampled(void);

	inline double GetMin() const { return m_lfMin; }
	inline double GetMax() const { return m_lfMax; }

	virtual bool GetVoxelInterpretation() const { return false; }

	inline double GetMean() const { return m_lfMean; }
	inline double GetStdDev() const { return m_lfStdDev; }
	inline double GetMeanSqrd() const { return m_lfExpSqrd; } // returns the mean of the squares, not the square of the mean

	inline int GetNumValidSamples() const { return m_nNumValidSamples; }

	virtual double FvgGetMin() const { return GetMin(); }
	virtual double FvgGetMax() const { return GetMax(); }

	virtual double FvgGetMean() const { return GetMean(); }
	virtual double FvgGetStdDev() const { return GetStdDev(); }
	virtual double FvgGetMeanSqrd() const { return GetMeanSqrd(); }

	bool ConvertFromRawVolGrid(const RawVolGrid & rawVolGrid, bool bIsBigEndian, bool bFlip=false);

	bool LoadFromPreprocessedFile(const char * fName, int nX, int nY, int nZ);

	bool DumpToPreprocessedFile(const char * fName) const;

	virtual void SetWorkingChannel(int nChannel) { m_nWorkingChannel = nChannel; }

	virtual int GetDataBounds(int) const;
	virtual mdsVector<2, double> GetSpaceBounds(int) const;
	virtual float GetValAt(int, int, int) const;
	virtual float GetValAt(double, double, double) const;
	virtual void SetValAt(int, int, int, float);
	virtual mdsVector<3, double> EvalGradientAt(double, double, double) const;
};

#include "viewInfo.h"

class VolumeViz : public ICustomDraw
{
	FloatVolGrid * m_pVolGrid;
	int m_nRenderMode;
	int m_nStepSize[3];
	double m_lfStdDev;
	double m_lfMean;

	double m_lfTransferOffset;
	double m_lfTransferWidthScale;

	static viewInfo m_gDummyViewInfo;

	viewInfo * m_pViewInfo;

	void GetVolStats()
	{
		m_lfStdDev = m_pVolGrid->FvgGetStdDev();
		m_lfMean = m_pVolGrid->FvgGetMean();
	}
public:
	VolumeViz();
	VolumeViz(FloatVolGrid & volGrid);
	~VolumeViz();

	void SetVolGrid(FloatVolGrid & volGrid) { m_pVolGrid = &volGrid; GetVolStats(); }

	void SetViewInfo(viewInfo & setTo) { m_pViewInfo = &setTo; }

	void SetStepSize(int nAxis, int nStepSize) { m_nStepSize[nAxis] = nStepSize; }

	void SetRenderMode(int nRenderMode) { m_nRenderMode = nRenderMode; }

	void SetTransferOffset(double lfTransferOffset) { m_lfTransferOffset = lfTransferOffset; }
	void SetTransferWidth(double lfTransferWidth) { m_lfTransferWidthScale = lfTransferWidth; }

	double GetTransferOffset() const { return m_lfTransferOffset; }
	double GetTransferWidth() const { return m_lfTransferWidthScale; }

	// ICustomDraw implementation
	virtual void Draw();
};

#endif